//============================================================================
// Name        : URLNormalizer.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <vector>
#include <list>
#include "../classes_net/URL.h"
#include "URLNormalizer.h"
#include "../classes_logger/DebugLogger.h"

using namespace std;

//DebugLogger dbug_normalize("dbug_normalize");

URLNormalizer::URLNormalizer() {
	try{
	defaultDirectoryIndexes.push_back("index.html");
	defaultDirectoryIndexes.push_back("index.jsp");	
	defaultDirectoryIndexes.push_back("index.shtml");
	defaultDirectoryIndexes.push_back("index.asp");
	defaultDirectoryIndexes.push_back("index.aspx");
	defaultDirectoryIndexes.push_back("index.php");
	defaultDirectoryIndexes.push_back("index.htm");
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


std::list<URL> URLNormalizer::normalize(const std::list<string> originalURLList) {
	std::list<URL> normalizedList;
	try{
	std::list<string>::const_iterator it;
	for ( it = originalURLList.begin() ; it != originalURLList.end(); it++ )
	    normalizedList.push_back(normalize(*it));
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
	return normalizedList;
}

URL URLNormalizer::normalize(const string originalURL) {

	try{
	//cout << "AAAAAAAA: " << originalURL << endl;
//	if (originalURL.size() <= 1){
//		URL url()
//	}
	string normalizedURL = originalURL;
	string domain = "";
	string path = "";
	string protocol = "";
	toLower(normalizedURL);
//	cout << "URL1: " << normalizedURL << endl;
	capitalizeEscapeSequences(normalizedURL);
//	cout << "URL2: " << normalizedURL << endl;
	removeFragmentAndQuestionMark(normalizedURL);
//	cout << "URL3: " << normalizedURL << endl;
	addTrailing(normalizedURL);
//	cout << "URL4: " << normalizedURL << endl;
	
	protocol = getProtocol(normalizedURL);
	uint slashIndex = normalizedURL.find("/");
	if (slashIndex != string::npos) {
		domain = normalizedURL.substr(0, slashIndex);
		path = normalizedURL.substr(slashIndex + 1, normalizedURL.size());
	}
	else {
		cout << "ERROR: URL had no \"/\""<< endl;
	}
//	cout << "PATH1: " << path << endl;
	removeDefaultPort(domain);
	addTrailing(domain);
	if (path != "") {
		removeDotSegments(path);
//		cout << "PATH2: " << path << endl;
		removeDirectoryIndexes(path);
//		cout << "PATH3: " << path << endl;
		removeTrailing(path);
//		cout << "PATH4: " << path << endl;
	}
//	cout << "protocol: " << protocol << endl;
//	cout << "domain: " << domain << endl;
//	cout << "path: " << path << endl;
	URL url(domain, path, protocol);
//	dbug_normalize.infoMsg(originalURL);
	return url;
	}catch(...){
//		cout << "Mc mandou mal, link quebrado: " << originalURL << endl;
		
//		dbug_normalize.errorMsg(originalURL);
		
//		URL url_google("www.google.com/", "", "http://");
//		return url_google;
		URL url_null(" ", "", "http://");
		return url_null;	
		
	}
}

inline void URLNormalizer::toLower(string & str) {
	try{
	for (uint i = 0; i < str.size(); i++)
		str[i] = tolower(str[i]);
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}

inline string URLNormalizer::getProtocol (string  & str) {
	try{
	uint index = str.find("://");
	string protocol = "";
	if (index != string::npos) { 
		protocol = str.substr(0, index + 3);
		str = str.substr( index+3, str.size() );
		//cout << "BAGGIO1: " << str << endl;;
		//cout << "BAGGIO1: " << protocol << endl;;
		return protocol;
	}
	else {
//		cout << "BAGGIO else"<< endl;
		return "http://";
	}
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
		return "http://";
	}
}

inline void URLNormalizer::addTrailing (string &str) {
	try{
	if(str[str.size() - 1] !=  '/')
		str = str + "/";
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}

inline void URLNormalizer::removeTrailing (string &str) {
	try{
	if(str[str.size() - 1] ==  '/')
		str = str.substr(0, str.size() - 1);
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}

/**
 * OBS: s� remove o diret�rio se ele for a �ltima coisa que aparece na string
 * ex: www.google.com/index.html => www.google.com/
 *     www.google.com/index.html?a=3 => www.google.com/index.html?a=3/
 */
inline void URLNormalizer::removeDirectoryIndexes(std::string & str) {
	try{
	for (uint i = 0; i < defaultDirectoryIndexes.size(); i++) {
		string defaultIndex = defaultDirectoryIndexes[i];
		string strEnd = "";
		//cast pq as coisinhas retornam uint, e nao int - sugou ateh descobrir
		if ((int)(str.size() - defaultIndex.size() - 1)  > 0) 
			strEnd = str.substr(str.size() - (defaultIndex.size() + 1) );
		if (strEnd.find(defaultIndex) != string::npos)
			str = str.substr(0, str.size() - (defaultIndex.size() + 1));
	}
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


inline void URLNormalizer::capitalizeEscapeSequences(std::string & str) {
	try{
	for (uint i = 0; i < str.size(); i++) {
		if(str[i] == '%') 
			if (i < str.size()-2) {
				str[i+1] = toupper(str[i+1]);
				str[i+2] = toupper(str[i+2]);;
			}
	}
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}



inline void URLNormalizer::removeFragmentAndQuestionMark(std::string & str) {
	try{
	if (str.find("#") != string::npos)
		str = str.replace(str.find("#"), str.size(), "", 0, 0); // remove tudo depois de #
	if (str.find("?") != string::npos)
		str = str.replace(str.find("?"), str.size(), "", 0, 0); // remove tudo depois de ?
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


inline void URLNormalizer::removeDefaultPort(std::string & str) {
	try{
	string strEnd = str.substr(str.size() - 3 );
	if (strEnd.find(":80") != string::npos) 
		str.erase(str.size() - 3 );
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


inline void URLNormalizer::removeDotSegments(std::string & str) {
	try{
	string aux = "";
	while (str != "") {
		//cout << "inicio do while str: " << str << endl;
		//cout << "inicio do while aux: " << aux << endl;
		if (str.find("./") == string::npos) {
			aux = str;
			break;
		}
		if (str.find("../") == 0) {
//			cout << "1" << endl;
			str = str.substr(3, str.size());
		}
		else if (str.find("./") == 0) {
//			cout << "2" << endl;
			str = str.substr(2, str.size());
		}
		else if (str.find("/./") == 0) {
//			cout << "3" << endl;
			str = str.substr(2, str.size());
		}
		else if (str == "/.") {
//			cout << "4" << endl;
			str = str.substr(1, str.size());
			str[0] = '/';
		}
		
		//Condi��es de volta de diret�rio
		else if (str.find("/../") == 0) {
//			cout << "5" << endl;
			str = str.substr(3, str.size());
			sobeDiretorio (aux);
		}
		else if (str == "/..") {
//			cout << "6" << endl;
			str = str.substr(2, str.size());
			str[0] = '/';
			sobeDiretorio (aux);
		}
		else if (str == "." || str == ".." ) {
//			cout << "7" << endl;
			str = "";
		}
		else {
//			cout << "8" << endl;
			
			bool barraNoComeco = false;
			if (str == "/") 
				str = "";
			uint slashIndex = str.find("/");
			// se str come�a com "/", procurar a segunda "/"
			if (slashIndex == 0)  {
//				cout << "8.1 " << str << endl;
				slashIndex = (str.substr(1,str.size())).find("/") + 1;
				barraNoComeco = true;
			}
			
			//Se encontrou a "/" sem ser na posi��o inicial
			if (slashIndex != string::npos) {
//				cout << "8.2" << endl;
				//if (barraNoComeco) {
					//cout << "8.2.1" << endl;
					//aux += str.substr(1, slashIndex);
				//}
				//else {
				//	cout << "8.2.2" << endl;
					aux += str.substr(0, slashIndex);
				//}
				str  = str.substr(slashIndex, str.size());
			}
			else{
//				cout << "8.3" << endl;
				aux += str;
				str = "";
			}
//			cout << "AUX: " << aux << endl;
		}
		//cout << "fim do while str: " << str << endl;
		//cout << "fim do while aux: " << aux << endl;
//		int i;
//		cin >> i;
	}
	str = aux;
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


inline void URLNormalizer::sobeDiretorio(std::string & str) {
	try{
	if (str == "")
		return;
	uint slashIndex = str.rfind("/");
	if (slashIndex == string::npos)
		str = "";
	else {
		str = str.substr(0, slashIndex);
	}
	}catch(...){
		// cout << "Baggio estragando a brincadeira... " << endl;
	}
}


//
//URLNormalizer::URLNormalizer() {
//	defaultDirectoryIndexes.push_back("index.html");
//	defaultDirectoryIndexes.push_back("index.jsp");	
//}
//
//
//std::list<URL> URLNormalizer::normalize(const std::list<string> originalURLList) {
//	std::list<URL> normalizedList;
//	std::list<string>::const_iterator it;
//	for ( it = originalURLList.begin() ; it != originalURLList.end(); it++ )
//	    normalizedList.push_back(normalize(*it));
//	return normalizedList;
//}
//
//URL URLNormalizer::normalize(const string originalURL) {
//	//cout << "AAAAAAAA" << endl;
//	string normalizedURL = originalURL;
//	string domain = "";
//	string path = "";
//	string protocol = "";
//	toLower(normalizedURL);
//	capitalizeEscapeSequences(normalizedURL);
//	removeFragmentAndQuestionMark(normalizedURL);
//	addTrailing(normalizedURL);
//	
//	protocol = getProtocol(normalizedURL);
//	uint slashIndex = normalizedURL.find("/");
//	if (slashIndex != string::npos) {
//		domain = normalizedURL.substr(0, slashIndex);
//		path = normalizedURL.substr(slashIndex + 1, normalizedURL.size());
//	}
//	else {
//		cout << "ERROR: URL had no \"/\""<< endl;
//	}
//	
//	removeDefaultPort(domain);
//	addTrailing(domain);
//	if (path != "") {
//		removeDotSegments(path);
//		removeDirectoryIndexes(path);
//		removeTrailing(path);
//	}
//	cout << "protocol: " << protocol << endl;
//	cout << "domain: " << domain << endl;
//	cout << "path: " << path << endl;
//	URL url(domain, path, protocol);
//	return url;
//}
//
//inline void URLNormalizer::toLower(string & str) {
//	for (uint i = 0; i < str.size(); i++)
//		str[i] = tolower(str[i]);
//}
//
//inline string URLNormalizer::getProtocol (string  & str) {
//	uint index = str.find("://");
//	string protocol = "";
//	if (index != string::npos) { 
//		protocol = str.substr(0, index + 3);
//		str = str.substr( index+3, str.size() );
//		//cout << "BAGGIO1: " << str << endl;;
//		//cout << "BAGGIO1: " << protocol << endl;;
//		return protocol;
//	}
//	else {
//		cout << "BAGGIO else"<< endl;
//		return "http://";
//	}
//}
//
//inline void URLNormalizer::addTrailing (string &str) {
//	if(str[str.size() - 1] !=  '/')
//		str += "/";
//}
//
//inline void URLNormalizer::removeTrailing (string &str) {
//	if(str[str.size() - 1] ==  '/')
//		str = str.substr(0, str.size() - 1);
//}
//
///**
// * OBS: s� remove o diret�rio se ele for a �ltima coisa que aparece na string
// * ex: www.google.com/index.html => www.google.com/
// *     www.google.com/index.html?a=3 => www.google.com/index.html?a=3/
// */
//inline void URLNormalizer::removeDirectoryIndexes(std::string & str) {
//	for (uint i = 0; i < defaultDirectoryIndexes.size(); i++) {
//		string defaultIndex = defaultDirectoryIndexes[i];
//		string strEnd = "";
//		//cast pq as coisinhas retornam uint, e nao int - sugou ateh descobrir
//		if ((int)(str.size() - defaultIndex.size() - 1)  > 0) 
//			strEnd = str.substr(str.size() - (defaultIndex.size() + 1) );
//		if (strEnd.find(defaultIndex) != string::npos)
//			str = str.substr(0, str.size() - (defaultIndex.size() + 1));
//	}
//}
//
//
//inline void URLNormalizer::capitalizeEscapeSequences(std::string & str) {
//	for (uint i = 0; i < str.size(); i++) {
//		if(str[i] == '%') 
//			if (i < str.size()-2) {
//				str[i+1] = toupper(str[i+1]);
//				str[i+2] = toupper(str[i+2]);;
//			}
//	}
//}
//
//
//
//inline void URLNormalizer::removeFragmentAndQuestionMark(std::string & str) {
//	if (str.find("#") != string::npos)
//		str = str.replace(str.find("#"), str.size(), "", 0, 0); // remove tudo depois de #
//	if (str.find("?") != string::npos)
//		str = str.replace(str.find("?"), str.size(), "", 0, 0); // remove tudo depois de ?
//}
//
//
//inline void URLNormalizer::removeDefaultPort(std::string & str) {
//	string strEnd = str.substr(str.size() - 3 );
//	if (strEnd.find(":80") != string::npos) 
//		str.erase(str.size() - 3 );
//}
//
//
//inline void URLNormalizer::removeDotSegments(std::string & str) {
//	string aux = "";
//	while (str != "") {
//		//cout << "inicio do while str: " << str << endl;
//		//cout << "inicio do while aux: " << aux << endl;
//		if (str.find("../") == 0) {
//			cout << "1" << endl;
//			str = str.substr(3, str.size());
//		}
//		else if (str.find("./") == 0) {
//			cout << "2" << endl;
//			str = str.substr(2, str.size());
//		}
//		else if (str.find("/./") == 0) {
//			cout << "3" << endl;
//			str = str.substr(2, str.size());
//		}
//		else if (str == "/.") {
//			cout << "4" << endl;
//			str = str.substr(1, str.size());
//			str[0] = '/';
//		}
//		
//		//Condi��es de volta de diret�rio
//		else if (str.find("/../") == 0) {
//			cout << "5" << endl;
//			str = str.substr(3, str.size());
//			sobeDiretorio (aux);
//		}
//		else if (str == "/..") {
//			cout << "6" << endl;
//			str = str.substr(2, str.size());
//			str[0] = '/';
//			sobeDiretorio (aux);
//		}
//		else if (str == "." || str == ".." ) {
//			cout << "7" << endl;
//			str = "";
//		}
//		else {
//			cout << "8" << endl;
//			
//			bool barraNoComeco = false;
//			if (str == "/") 
//				str = "";
//			uint slashIndex = str.find("/");
//			// se str come�a com "/", procurar a segunda "/"
//			if (slashIndex == 0)  {
//				slashIndex = (str.substr(1,str.size())).find("/") + 1;
//				barraNoComeco = true;
//			}
//			
//			//Se emcontrou a "/" sem ser na posi��o inicial
//			if (slashIndex != string::npos) {
//				if (barraNoComeco)
//					aux += str.substr(1, slashIndex);
//				else
//					aux += str.substr(0, slashIndex);
//				str  = str.substr(slashIndex, str.size());
//			}
//			else{
//				aux += str;
//				str = "";
//			}
//		}
//		//cout << "fim do while str: " << str << endl;
//		//cout << "fim do while aux: " << aux << endl;
////		int i;
////		cin >> i;
//	}
//	str = aux;
//}
//
//
//inline void URLNormalizer::sobeDiretorio(std::string & str) {
//	if (str == "")
//		return;
//	uint slashIndex = str.rfind("/");
//	if (slashIndex == string::npos)
//		str = "";
//	else {
//		str = str.substr(0, slashIndex);
//	}
//}
//
////int main() {
////	URLNormalizer normalizer;
////	cout << normalizer.normalize("wWw.GooGle.coM:80/.././../a/./b/.././../../d/./index.html#avd") << endl; // prints !!!Hello World!!!
////	
////	return 0;
////}
